(********************************************************************)
(*	file: 		ADSPListen.p										*)
(*	version:	0.5alpha											*)
(* 																	*)
(* Perform the following actions: 									*)
(* 	(1) Listen for incoming calls on the connection listener		*)
(*	(2) Check to see if any outgoing calls have been answered		*)
(*	(3) Check connections for incoming data							*)
(* 	(4) Check connections for attention messages					*)
(*																	*)
(* In:	params[1] =	name of the global message to call hypercard 	*)
(*					with on incoming messages						*)
(*																	*)
(* Defaults: container 	= HyperADSPData								*)
(*			 message 	= none.										*)
(* 																	*)
(* ****************************************************************	*)
(* *				--- Planned Upgrades: ---					  *	*)
(* *															  *	*)
(* *	(2) User definable callback containers and a default 	  * *)
(* *		messages for incoming calls (messages are currently	  * *)
(* *		attached to outgoing calls only				  	  	  * *)
(* *															  *	*)
(* ****************************************************************	*)
(*																	*)
(* ----------------------------------------------------------------	*)
(* By:		Donald Koscheka, Nancy Rosenberg						*)
(*			Thanks to Tim Warden for the use of "fone.p" which 		*)
(*			served as a model for the HyperDataStream package.		*)
(* Date:	4-Feb-88												*)
(* 			©	Copyright 1988, Apple Computer, Inc.				*)
(*			All Rights Reserved										*)
(*																	*)
(* ----------------------------------------------------------------	*)
(*						Modification History						*)
(* ----------------------------------------------------------------	*)
(*  Date	| By	|	 				Description					*)
(* ----------------------------------------------------------------	*)
(*  4-Feb-88| DK	| file created									*)
(* 17-Mar-88| DK	| ALPHA RELEASE									*)
(* 18-Mar-88| DK	| Added callback to inform of incoming calls	*)
(* 20-Apr-88| DK	| Adjust the size of read data to actCount and	*)
(*			|		| append a zero to the end of the data.			*)
(* ----------------------------------------------------------------	*)
(********************************************************************)

(*******************************************************************
							Build Sequence

pascal -o "{hpo}"ADSPListen.p.o "{adsp}"ADSPListen.p
link -m ENTRYPOINT  -rt XCMD=1303 -sn Main=ADSPListen¶
	"{hpo}"ADSPListen.p.o¶
	"{libraries}"Interface.o ¶
	"{plibraries}"paslib.o¶
	-o "{hp}"HyperADSPTest
		 
*******************************************************************)


{$R-}
{$S ADSPListen}
UNIT Koscheka;

(*******************************************************************)
INTERFACE 
(*******************************************************************)


Uses MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, HyperXCmd, AppleTalk, ADSP, adspxcmd;


Procedure EntryPoint( paramPtr : XCmdPtr );



(*******************************************************************)
IMPLEMENTATION
(*******************************************************************)


PROCEDURE ADSPListen( paramPtr: XCmdPtr ); FORWARD;

Procedure EntryPoint( paramPtr : XCmdPtr );
	Begin
		ADSPListen( paramPtr );
	End;
	
	
Procedure ADSPListen( paramPtr : XCmdPtr );
VAR
	myFlags	: SignedByte;	{ user flags from an incoming an unsolicited message	}
	adsp	: ADSPPtr;		{ pointer to the memory used by HyperdataStream			}
	error	: OSErr;		
	err		: OSErr;
	cn,nn	: cbPtr;		{ ptr to connection blocks of established connections	}
	nbp		: NBPPtr;		{ pointer to the memory used by HyperAppleTalk's NBP	}
	mybPtr	: Ptr;			{ pointer to byte for use in getting and settign flags	}
	
	
{$I XCmdGlue.inc }
{$I XCMDADSP.inc }	

PROCEDURE RespondToListen;
(************************************************)
(* If the connection listener has completed, 	*)
(* then someone is trying to call.  If we can	*)
(* allocate and initialize a connection, go		*)
(* ahead and acknowledge receipt of the call.	*)
(************************************************)
VAR
	err		: Integer;
	conn	: CBPtr;
	tH		: Handle;
	
BEGIN
	IF  adsp^.pb.ioResult = noErr THEN {*** have an incoming call		***}
		BEGIN
			{*** Create a connection end to the caller and set the		***}
			{*** state to active indicating the connection is accepted	***}
			conn := DSPCreate(adsp^.pb.remoteAddress,ATPBSIZE,ATPBSIZE,RETRY,INTERVAL,NIL );
			
			IF conn <> NIL THEN
			WITH conn^.dspPB DO	{*** acknowledge connection request	***}
				BEGIN
					ioCRefNum 				:= adsp^.dspRefNum;
					ioCompletion			:= NIL;
					csCode					:= dspOpen;
					ocMode					:= ocAccept;
					ccbRefNum				:= conn^.ccbRef;
					remoteCID				:= adsp^.pb.remoteCID;
					localCID				:= 0;
					remoteAddress			:= adsp^.pb.remoteAddress;	
					filterAddress.aNet		:= 0;
					filterAddress.aNode		:= 0;
					filterAddress.aSocket 	:= 0;
					RecvSeq					:= 0;
					attnRecvSeq				:= 0;
					SendSeq					:= adsp^.pb.SendSeq;
					attnSendSeq				:= adsp^.pb.attnSendSeq;
					SendWindow				:= adsp^.pb.SendWindow;
					ocInterval				:= INTERVAL;
					ocMaximum				:= RETRY;
					err := PBControl( @conn^.dspPB, ASYNC );
					
					{*** 3/18	Now call back Hypercard to inform 	***}
					{***		it of an incoming call.				***}
							
					conn^.remName := NBPGetName( adsp^.pb.remoteAddress );
					IF conn^.remName = NIL THEN	{*** try to discover the name 	***}
						BEGIN
							nbp := NBPPtr(RetrieveHandle(GLOBALNBPDATA));
							IF nbp <> NIL THEN 
								BEGIN
									SendCardMessage('DoALookUp');
									conn^.remName := NBPGetName( adsp^.pb.remoteAddress );
								END;
						END;
				END;
					
			{*** Now reset connection listener to wait for next call	***}
			err := PBControl( @adsp^.pb, ASYNC );
		END
END;


PROCEDURE ReadADSPData( conn : CBPtr; cnt :  Integer; callMe : Handle );
(************************************************)
(* Poll the connection to determine whether any	*)
(* data is pending.  If so, read it and pass it	*)
(* back to hypercard via the callback mechanism.*)
(*												*)
(* IN:	conn 	: pointer to the connection 	*)
(*		cnt		: number of bytes to read		*)
(*		callMe	: method to invoke on callback	*)
(************************************************)
VAR
	Tempb	: dspParamBlock;
	err		: Integer;
	temp	: LongInt;
	inSize	: LongInt;
	globName: Str255;	
	tp		: Ptr;
	tH		: Handle;
	
BEGIN
	globName:= 'HyperADSPData';
	
	WITH conn^ DO
		BEGIN	{*** read the data ***}
			IF inBuf = NIL THEN
				BEGIN
					inBuf := NewHandle( LongInt(cnt) );
					MoveHHi( inBuf );
					HLock( inBuf );
					tp	:= inBuf^;
				END
			ELSE			{*** inBuf already allocated, add to end of it	***}
				BEGIN
					HUnlock( inBuf );
					inSize 	:= GetHandleSize(inBuf);
					temp 	:= inSize;
					inSize 	:= inSize + LongInt( cnt );
					SetHandleSize( inBuf, inSize );
					MoveHHi( inBuf );
					HLock( inBuf );
					temp 	:= temp + Ord4( inBuf^ );
					tp		:= Pointer(temp);
				END;
		
			WITH Tempb DO	{*** now read the data ***}
				BEGIN
					ioCRefNum 	:= adsp^.dspRefNum;
					ccbRefNum 	:= conn^.ccbRef;
					csCode 		:= dspRead;
					reqCount	:= cnt;
					dataPtr 	:= tp;			
				END;
			err := PBControl( @Tempb, SYNC );
			HUnlock( inBuf );

			IF err=noErr THEN
				BEGIN
					inSize := GetHandleSize( inBuf );
					inSize := inSize - (cnt-Tempb.actCount);
					SetHandleSize( inBuf, inSize );
				END;
			
			IF (err=noErr) AND (Tempb.eom=1) THEN
				BEGIN
					IF inBuf <> NIL THEN {*** Call back with data	***}
						BEGIN
							IF remName <> NIL THEN
								SetGlobal( 'ADSPCaller', remName );

							tp   	:= Pointer( Ord4( inBuf^ ) + LongInt(inSize) );
							tp^		:= 0;
							
							SetGlobal( globName, inbuf );
							DisposHandle( inbuf );
							inbuf := NIL;
						END;
						
					
					IF callMe <> NIL THEN {*** Set remote-end name	***}
						BEGIN
							HLock( callMe );
							ZeroToPas( Pointer( callMe^ ), globName );
							HUnlock( callMe );
							SendCardMessage(  globName );
						END;
						
				END;
		END;{*** read the data ***}
END;



Procedure CancelADSPRead( cb : cbPtr );
(************************************)
(* Forward Reset was received from a*)
(* remote connection, dump the 		*)
(* incoming data (if any).			*)
(*									*)
(************************************)
BEGIN
	{*** note to N.R. Do we also need to reset the receive sequence ??? ***}
	WITH cb^ DO
		BEGIN
			HUnlock( inbuf );
			DisposHandle( inbuf );
			inbuf := NIL; 
		END;
END;



Function CheckMessages( cb : cbPtr ): OSErr;
(************************************)
(* check this connection for:		*)
(* (1) Completed Incoming Messages	*)
(* (2) Completed Outgoing Messages	*)
(************************************)
VAR
	myErr		: OSErr;
	tpb		: dspParamBlock;
BEGIN
	WITH tpb DO
		BEGIN
			csCode 		:= dspStatus;
			ioCRefNum	:= adsp^.dspRefNum;
			ccbRefNum	:= cn^.ccbRef;
		END;
	myErr := PBControl( @tpb, SYNC );
			
	IF myErr = noErr THEN
		BEGIN
			{*** (3) Check for pending incoming data	***}
			IF tpb.recvQPending <> 0  THEN
				ReadADSPData( cn, tpb.recvQPending, paramPtr^.params[1] );

			{*** (4) See if any write operations have completed	***}
			IF (tpb.SendQPending = 0) AND (cn^.OutBuf <> NIL ) THEN
				BEGIN
					HUnlock( cn^.outBuf );
					DisposHandle( cn^.outBuf );
					cn^.outBuf := NIL;
				END;
		END; { myErr <> noErr }
	CheckMessages := myErr;
END;


Procedure PayAttention( cb : cbPtr );
(************************************)
(* received an attention message 	*)
(* a remote connection, pass it back*)
(* to Hypercard...					*)
(************************************)
VAR
	attnMsg	: Str255;		{ string for callback of received attention messages	}
	attnHand: Handle;		{ temp handle for retrieving attention messages			}
BEGIN
	WITH cb^.ccb DO
		BEGIN
			attnHand := NewHandle( LongInt(attnSize) );
			IF attnHand <> NIL THEN
			BEGIN
				HLock( attnHand );
				BlockMove( attnPtr, attnHand^, LongInt(attnSize));
				ZeroToPas( attnHand^, attnMsg );
				SendCardMessage( attnMsg );
				HUnlock( attnHand );
				DisposHandle( attnHand );
			END
		END;
END;


Procedure KillAttention( cb : CBPtr );
(************************************)
(* If an asynchronous attention 	*)
(* message has completed, go ahead	*)
(* and eliminate the memory that it	*)
(* was using...						*)
(************************************)
BEGIN
	WITH cb^ DO
		IF attnBuf <> NIL THEN
		BEGIN
			HUnlock(attnBuf);
			DisposHandle(attnBuf);
			attnBuf := NIL;
			attnPB.ioResult := 1;	{*** make it look busy ***}
		END;
END;



(************************************************************************************)
(* 										ADSPListen									*)
(************************************************************************************)

BEGIN
	error	:= noErr;
	adsp	:= ADSPPtr(RetrieveHandle( GLOBALDSPDATA ));

	IF adsp <> NIL THEN
		BEGIN
			adsp^.checkPoint := RECEIVING;
			cn := adsp^.ends;

			WHILE cn <> NIL DO		{*** (2) Monitor status on all connections	***}
				WITH cn^ DO 
					BEGIN
						nn := next;
						err := noErr;
						
						CASE ccb.state OF { connection states } 
							sClosed:	err := DSPHangUp( cn );
										
							sOpen:
							BEGIN
								IF mode = NOP THEN {*** connection is opening 	***}
									BEGIN	{*** inform Hypercard of connection	***}
										SetGlobal( 'ADSPCaller', remName );
										SendCardMessage( 'ConnectionMade' );
										mode := EST;
									END
								ELSE
								BEGIN 		{*** Check an established connection ***}
									myBPtr := @cn^.ccb.userFlags;
									
									IF BAND(myBPtr^, eClosed)=eClosed THEN 
										err := DSPHangUp( cn )
									ELSE
									BEGIN
										IF BAND(myBPtr^,eAttention)=eAttention THEN
											PayAttention( cn );
											
										IF BAND(myBPtr^,eFwdReset)=eFwdReset THEN 
											CancelADSPRead( cn ); 
			
										IF(attnPB.ioResult<=0) AND (attnBuf<>NIL)THEN
											KillAttention( cn );
											
										err := CheckMessages( cn );
										
										myBPtr^ := 0;		{ clear user flags}
									END;
								END;
							END;
						END; { case of connection states }
						
						IF err <> noErr THEN error := err;
						cn := nn;
					END;
			RespondToListen;		{*** check for opening calls ***}
			adsp^.checkPoint := CLOSE_OK;
		END; {*** IF adsp <> NIL ***}

	paramPtr^.returnValue := ErrorReturn( error );
END;

end.




